#include "pch.hpp"
#include "Reflection.hpp"




StaticReflection::StaticReflection(const StaticReflectionElement* table, std::size_t totalSize)
	: m_table(table)
	, m_elements(0)
	, m_totalSize(totalSize)
{
	if(0 == m_table)
		BOOSTEXT_THROW(programming_error("A ReflectionTable may not be empty"));

	if(0 == m_totalSize)
		BOOSTEXT_THROW(programming_error("A Structure that is being reflected may not be empty"));

	std::size_t offset = 0;
	while(m_table[m_elements].semanticName)
	{
		const StaticReflectionElement& element = m_table[m_elements];

		offset += element.relativeOffset;
		offset += Format::size(element.format);

		++m_elements;
	}

	if(offset > m_totalSize)
		BOOSTEXT_THROW(programming_error(format("Reflection table indicates a bigger size than it was passed to the constructor: Table %1%, actual %2%") % m_totalSize % offset));
}
///////////////////////////////////////////////////////////////////////////////////////////////////




std::size_t StaticReflection::elements() const
{
	return m_elements;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

std::size_t StaticReflection::totalSize() const
{
	return m_totalSize;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

const StaticReflectionElement& StaticReflection::operator[](std::size_t index) const
{
	if(index >= m_elements)
		BOOSTEXT_THROW(out_of_range_exception(index, 0, m_elements - 1));

	return m_table[index];
}
///////////////////////////////////////////////////////////////////////////////////////////////////




bool StaticReflection::operator==(const StaticReflection& that) const
{
	bool thesame = m_table == that.m_table;

	if(thesame)
	{
		// When both tables are "equal" they should have the same number of elements
		// When they have not, it's a programming error on my side
		if(m_elements != that.m_elements)
			BOOSTEXT_THROW(programming_error(format("Both tables are the same, but have a different number of elements: '%1%' - '%2%'") % m_elements % that.m_elements));

		// When both tables are "equal" they should have the same total size
		// When they have not, it's the error of the programmer who used it
		if(m_totalSize != that.m_totalSize)
			BOOSTEXT_THROW(exception(format("Both tables are the same, but have different sizes: '%1%' - '%2%'") % m_totalSize % that.m_totalSize));
	}

#ifdef _DEBUG
	#define DETAILED_TEST
#endif

#ifdef DETAILED_TEST
	// This detailed test verifies that both layouts are equal once their pointer
	// to the ElementLayout table is the same. Shouldn't be required in release mode.
	if(thesame)
	{
		// Now compare each entry in the tables
		for(uint32 i = 0; i < m_elements; ++i)
		{
			if(strcmp(m_table[i].semanticName, that.m_table[i].semanticName) != 0)
				BOOSTEXT_THROW(programming_error(format("Both tables are the same, but element %1% differs: '%2%' - '%3%'") % i % m_table[i].semanticName % that.m_table[i].semanticName));

			if(m_table[i].semanticIndex != that.m_table[i].semanticIndex)
				BOOSTEXT_THROW(programming_error(format("Both tables are the same, but element %1% differs: '%2%' - '%3%'") % i % m_table[i].semanticIndex % that.m_table[i].semanticIndex));

			if(m_table[i].relativeOffset != that.m_table[i].relativeOffset)
				BOOSTEXT_THROW(programming_error(format("Both tables are the same, but element %1% differs: '%2%' - '%3%'") % i % m_table[i].relativeOffset % that.m_table[i].relativeOffset));

			if(m_table[i].format != that.m_table[i].format)
				BOOSTEXT_THROW(programming_error(format("Both tables are the same, but element %1% differs: '%2%' - '%3%'") % i % m_table[i].format % that.m_table[i].format));
		}
	}
#endif

	// The layout table MUST be allocated on the static storage of the application and thus
	// 2 InputSignatures are the same when they use the same table
	return m_table == that.m_table;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




Reflection::Reflection(std::size_t totalSize, std::size_t elements)
	: m_totalSize(totalSize)
{
	if(0 == elements)
		BOOSTEXT_THROW(programming_error("A ReflectionTable may not be empty"));

	if(0 == m_totalSize)
		BOOSTEXT_THROW(programming_error("A Structure that is being reflected may not be empty"));

	m_table.reserve(elements);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

std::size_t Reflection::elements() const
{
	return m_table.size();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

std::size_t Reflection::totalSize() const
{
	return m_totalSize;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Reflection::add(ReflectionElement element)
{
	std::size_t offset = 0;

	foreach(const ReflectionElement& element, m_table)
	{
		offset += element.relativeOffset;
		offset += Format::size(element.format);
	}

	offset += element.relativeOffset;
	offset += Format::size(element.format);
	if(offset > m_totalSize)
		BOOSTEXT_THROW(programming_error(format("The ReflectionElement '%1%' cannot be added to this Reflection: It exceeds the total size (%1%) of the reflected structure") % m_totalSize));

	m_table.push_back(element);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Reflection::add(Reflection& reflection)
{
	foreach(ReflectionElement& element, reflection.m_table)
	{
		add(element);
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool Reflection::operator==(const Reflection& that) const
{
	if(m_table.size() != that.m_table.size())
		return false;

	if(m_totalSize != that.m_totalSize)
		false;

	for(uint32 i = 0; i < m_table.size(); ++i)
	{
		if(m_table[i].semanticName != that.m_table[i].semanticName)
			false;

		if(m_table[i].semanticIndex != that.m_table[i].semanticIndex)
			false;

		if(m_table[i].relativeOffset != that.m_table[i].relativeOffset)
			false;

		if(m_table[i].format != that.m_table[i].format)
			false;
	}

	return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool Reflection::operator==(const StaticReflection& that) const
{
	if(m_table.size() != that.elements())
		return false;

	if(m_totalSize != that.totalSize())
		false;

	for(uint32 i = 0; i < m_table.size(); ++i)
	{
		if(m_table[i].semanticName != that[i].semanticName)
			false;

		if(m_table[i].semanticIndex != that[i].semanticIndex)
			false;

		if(m_table[i].relativeOffset != that[i].relativeOffset)
			false;

		if(m_table[i].format != that[i].format)
			false;
	}

	return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
